\chapter{Architecture du projet}
Le projet s'articule en divers élements qui communiquent chacun entre eux de manière différente. Voici une représentation simplifiée du fonctionnement de ces élements.
\begin{figure}[h!]
  \centering
    \includegraphics[width=0.8\textwidth]{schema_materiel.png}
  \caption{Schéma simplifié représentant l'ensemble des élements qui composent le système AirPi, et la façon dont ils communiquent.}
\end{figure}

Le Raspberry-Pi tient le rôle principal dans ce système de \emph{streaming} puisque c'est lui qui est le serveur : il est chargé de connecter les utilisateurs, recevoir leurs médias, fournir plusieurs informations à tous les utilisateurs (listes des utilisateurs et des médias, état de la lecture, etc...).

\section{Architecture matérielle}

\subsection{Le Raspberry-Pi}
C’est l’élément central du système, il permet de faire tourner tous les serveurs, à savoir le serveur Tomcat et le serveur \emph{smartphone}. Il contient également l’application AirPi qui gère toutes les informations issues des évènements des serveurs et de l’interface graphique (le \emph{Manager}).

\subsection{Les \emph{smartphones}}
Ils permettent à l’utilisateur d’utiliser l’applications Android dans le cas
d’un smartphone Android ou l’application iOS dans le cas d’un smartphone de la
marque Apple. Il communique avec le Raspberry-Pi au moyen du serveur smartphone donc au moyen de sockets.

\subsection{Les PC}
Ce sont les entités les plus passives puisqu’elles permettent à l’utilisateur de
profiter d'AirPi uniquement grâce au navigateur Internet (application web
fournie par Tomcat).
Aucune application supplémentaire n’est à installer.

\section{Architecture fonctionnelle}

\subsection{Les serveurs}

\subsubsection{La partie dédiée aux pages web}
Tous les échanges entre les clients utilisant un ordinateur et le Raspberry-Pi se font grâce à des requêtes HTTP (GET et POST).

\bigskip

Un serveur Tomcat a été mis en place : il écoute sur le port 8080. Il est chargé de délivrer les pages web aux clients. Grâce à ces pages, les clients ont accès à une interface de connexion, qui leur permet par la suite d'envoyer des médias, de consulter la liste des utilisateurs et des médias, d'effectuer diverses actions en fonction de leurs droits.

\bigskip

En ce qui concerne l'envoi des médias, nous avons cherché à récupérer et traiter directement le flux d'octets envoyés, sans passer par Tomcat qui ne nous autorise pas cela. De cette façon, nous avons le contrôle sur le fichier qui est en cours d'envoi, nous pouvons choisir où l'enregistrer et nous pouvons commencer la lecture avant même que le téléversement ne soit fini. Pour mettre en place un tel système, nous avons ouvert une \emph{socket} sur le port 8081. L'utilisateur effectue une requête POST sur cette socket depuis une page web proposée par Tomcat. L'URL est de la forme suivante : \textbf{http://ip\_raspberry:8081/ID} 
où ID est l'identifiant unique de chaque utilisateur, à la manière des
identifiants de session sur un serveur web (cela permet de les authentifier).

\subsubsection{La partie dédiée aux \emph{smartphones}}
Afin de permettre la gestion des smartphones de type iOS et Android,
l’application offre un service de gestion de session. Nous avons donc conçu un
serveur Java qui permet aux applications clientes déployées sur les
\emph{smartphones} de : se connecter, contrôler le lecteur de média s’ils en ont
les droits, supprimer des médias, et enfin ajouter des médias. Nous avons choisi
d’utiliser encore une fois le système des \emph{sockets} pour permettre la communication entre le serveur et les applications clientes. Le serveur est passif, c’est-à-dire qu’il se contente seulement de répondre aux requêtes envoyées par les clients. En aucun cas le serveur ne débute une \og conversation\fg{}. Une conversation se déroule donc de la manière suivante : le client émet une requête de la forme Nom\_Commande:param1;param2;nom\_user, puis le serveur répond avec le format suivant : ServerOK[Nom\_Commande]:data1;data2 dans le cas où il y a des paramètres et où la commande ne produit pas d’erreur. Dans le cas contraire le serveur répondra ServerKO[Nom\_Commande].


\subsection{Les applications mobiles}
De l’autre côté de cette architecture client/serveur intégrée au système nous avons les applications clientes \emph{smartphones}. Nous avons choisi de développer ces applications sur les système iOS et Android pour des raisons pratiques. En effet nous possédons des téléphones mobiles possédant ces OS et avions la possibilité d’apprendre la programmation d’applications destinées à ces périphériques. Ces applications ont pour rôle d’enrichir l'expérience utilisateur, notamment en proposant la détection automatique du serveur principal AirPi. Elles sont composées de plusieurs interfaces (ou vues) offrant les mêmes services que la page web.

\subsubsection{L'application Android}

\lstset{language=Java, breakindent=40pt, breaklines}
\paragraph{Fonctionnalités}
L'application Android a été réalisée à l'aide de l'IDE Eclipse. Lorsqu'on la lance, on tombe donc aussi sur une page de connexion, nécessitant un pseudonyme et un mot de passe, comme spécifié. Deux boutons sont accessibles en haut de l'écran : l'un permettant de relancer la recherche automatique du Raspberry-Pi, l'autre permettant de modifier les paramètres de connexion au Raspberry-Pi. Une case à cocher permet de sauvegarder son pseudonyme et son mot de passe, afin de ne pas avoir à les renseigner une nouvelle fois lors d'une connexion ultérieure. Un message d'erreur est affiché si le Raspberry-Pi n'est pas joignable, le mot de passe n'existe pas ou si le pseudonyme est déjà utilisé.

\bigskip

Une fois connecté, l'utilisateur arrive sur la page principale. Un bouton lui permet de sélectionner un fichier de son téléphone qu'il souhaite envoyer, et celui en dessous sert à l'envoyer au Raspberry-Pi. On trouve enfin une barre d'URL, dans laquelle l'utilisateur entre l'adresse d'une vidéo, hébergée sur un site de vidéos tel que Youtube, qui l'envoie au Raspberry-Pi afin qu'il télécharge la vidéo ou la musique en question. Vous pouvez vous référer à la figure 1.1, qui image cette situation.

\bigskip

L’identification de l’utilisateur déclenche une requête de connexion qui
déclenche elle-même l’authentification de l’utilisateur. Si l’authentification
est réussie, un objet \emph{User} est créé par l’objet \emph{Manager}. Ensuite,
l’utilisateur a la possibilité de sélectionner un média et de l’envoyer, ce qui
déclenche l’envoi du média sélectionné, au serveur (socket ouverte sur le port
8081 comme vu précédemment).
Voici une partie du code correspondant à l'envoi d'un fichier :

\begin{lstlisting}
Socket socket = new Socket(ipServ,8081);

//Envoie la requête HTTP au serveur				
OutputStream output = socket.getOutputStream();
PrintWriter pw = new PrintWriter(new OutputStreamWriter(output, "UTF-8"), true);

pw.println("POST /" + userID + " HTTP/1.1");
pw.println("Connection: keep-alive");
pw.println("Accept-Encoding: gzip, deflate");
pw.println("Content-Length: " + String.valueOf(file.length())); 
pw.println("Content-Type: multipart/form-data; boundary=" + twoHyphens + boundary + lineEnd + lineEnd + twoHyphens + boundary);//Les variables utilisées ici correspondent à des chaînes de caractères attendues par le serveur, notamment le boundary, qui est un repère permettant de connaître le début du fichier
pw.println("Content-Disposition: form-data; name=\"file\"; filename=\"" + file.getName() + "\"" + lineEnd);

//Lit le fichier octet par octet, puis l'envoie au serveur
FileInputStream fileInputStream = new FileInputStream(file);
byte[] buffer = new byte[10240];
int n = -1;
while((n = fileInputStream.read(buffer))!=-1) {
	output.write(buffer,0,n);
	output.flush();
}

//Permet de lire la réponse du serveur
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String inputLine;

while ((inputLine = in.readLine()) != null) {
	if (inputLine.contains("result")) 
		this.result = inputLine;
}
\end{lstlisting}

Suite à l’envoi du média, un objet \emph{Media} est créé, ce qui déclenche son ajout à la liste de lecture, puis, au lancement d’OMXPplayer, ce qui permet de lire le média sur le Raspberry-Pi.

\bigskip

S'il le souhaite, l'utilisateur peut aussi avoir accès à la liste des utilisateurs connectés en faisant glisser son doigt sur la gauche, ainsi qu'à la liste de lecture en le faisant glisser sur la droite. S'il est administrateur, il aura accès à la barre de contrôle de la liste de lecture, lui donnant la possibilité de mettre la lecture en pause, la stopper, passer à la suivante et augmenter ou baisser le volume. Dans les paramètres, la page de changement des mots de passe lui sera aussi accessible. Quelque soit son statut, l'utilisateur pourra toujours se déconnecter à l'aide du bouton à droite de celui des paramètres.

\paragraph{Mise en \oe{}uvre}
L'application Android communique avec le serveur via une \emph{socket}, sur le port 8082 du serveur \emph{smartphone}. Grâce à elle, l'application peut prévenir le serveur en cas de connexion d'un utilisateur, envoyer les requêtes correspondant aux commandes de la liste de lecture, comme la mise en pause ou la déconnexion d'un utilisateur. C'est aussi cette \emph{socket} qui est utilisée afin d'envoyer les URL.

\begin{figure}[h!]
  \centering
    \includegraphics[width=0.8\textwidth]{diagramme_rafraichissement.png}
  \caption{Schéma simplifié représentant les échanges pour le rafraîchissement des listes de l'application Android.}
\end{figure}

Comme le montre le diagramme de séquences ci-dessus, le rafraîchissement de l’affichage des listes d’utilisateurs et de médias se fait toutes les 5 secondes. Cela signifie que toutes les 5 secondes nous créons un objet Requête qui va se charger de créer la \emph{socket} sur le port 8082 afin de communiquer avec le serveur pour, enfin, récupérer la liste requise, mise à jour. Une fois que l’objet Requête a réceptionné la réponse du serveur, il se charge de notifier le changement de la liste à la vue, qui rafraîchit alors son affichage.

\bigskip

En ce qui concerne l'envoi des fichiers, cela se passe sur le port 8081 du serveur. L'application envoie une requête HTTP contenant notamment le type de requête (POST), la taille du fichier que l'on s'apprête à envoyer ainsi que son type. Ce dernier contient des informations permettant au serveur de savoir à quel endroit les données qu'il reçoit ne font plus partie de l'en-tête HTTP mais sont des fragments du fichier à lire. Une dernière permet au serveur de connaître le nom du fichier envoyé.

\bigskip

Le fichier est lu octet par octet, chaque octet étant directement envoyé au serveur.


\subsubsection{L'application iOS}

\lstset{language=[Objective]C, breakindent=40pt, breaklines}

\paragraph{Fonctionnalités}
Tout comme l’application Android, l’application iOS offre la possiblité de détecter automatiquement le Raspberry-Pi en scannant le réseau. L’utilisateur peut choisir d’activer ou de désactiver cette fonctionnalité, afin de pouvoir lancer ou non, de manière automatique, la détection du Raspberry-Pi (en terme d’adresse IP). L’utilisateur peut, s’il le souhaite, renseigner directement l’adresse IP du Raspberry-Pi. Une fois le pseudonyme, le mot de passe renseigné, et le bouton de connexion pressé, l’utilisateur se voit redirigé soit vers la vue administrateur ou soit vers la vue invité si l’authentification à réussi. Dans le cas contraire, un message d’erreur sera affiché.

\bigskip

L’interface principale (la vue chargé après l’étape de connexion) permet d’accéder principalement à la liste de lecture. Chaque cellule renseigne l’utilisateur sur le titre du média, la durée (si elle est disponible) et l’utilisateur qui à téléverser le média. Une petite image est ajoutée pour des raisons esthétiques. Si l’utilisateur est administrateur il peut commander la lecture grâce à la barre de lecture située en bas de l’écran. Cette barre se compose des boutons lecture, suivant, stop, augmenter le volume et diminuer le volume. L’utilisateur invité ne dispose pas de cette barre de contrôle.
En glissant sur le côté, ou en appuyant sur le bouton situé en haut à gauche, il peut afficher la liste des utilisateurs. Il peut également accéder aux paramètres de modification des mots de passe invité et administrateur s'il en a les droits. L’utilisateur dispose de la fonction \og \emph{logout}\fg{}. A noter : si l’utilisateur quitte l’application, il est automatiquement déconnecté.
Enfin, l’utilisateur peut ajouter un média en cliquant sur le bouton \og Add media\fg{}.  L’application iOS ne permet pas d’envoyer des médias stockés sur le périphérique de stockage de l’appareil. La politique d’Apple Inc. ne permet pas aux développeurs d’accéder au contenu logique des médias et cela dans le but de protéger les \oe{}uvres des artistes. C’est pourquoi l’application iOS propose uniquement le téléversement de liens. Pour cela, l’utilisateur dispose d’un moteur de recherche afin de trouver la vidéo issue des serveurs Youtube, par exemple, en indiquant les mots clés correspondants. En cliquant sur la vidéo qu’il souhaite téléverser, il provoquera l’ajout de celle-ci à la liste de lecture.

\paragraph{Mise en \oe{}uvre}
L’application a été developpée à l’aide de l’IDE xCode dédié au développement d’application Apple. Le langage utilisé est l’Objective-C. La syntaxe se rapproche de celle de Java ce qui facilite l’apprentissage de ce langage. La gestion des \emph{sockets} est primordiale afin de permettre une communication rapide et efficace entre l’application et le serveur. C’est pourquoi dans le projet, nous dédions une classe à la gestion des requêtes \emph{sockets} à destination du serveur \emph{smartphone}. La classe fonctionne de la manière suivante : l’objet instancié reçoit une requête à envoyer au serveur sous forme de \emph{String} (ou plutôt NSString), qu’il envoie au serveur en ayant créé une  \emph{sockets} au préalable. Le serveur répond, et génère de ce fait un \og \emph{StreamEvent}\fg{} qui déclenche une méthode de la classe \og \emph{Request}\fg{}. Cette méthode traite alors le résultat reçu en notifiant l’objet qui a généré la requête. On reconnaît ici l’utilisation du patron Observateur utilisé de la même manière dans l’application Android.

\bigskip

Concernant l’intégration d’un moteur de recherche vidéo Youtube, l’application
offre la possiblité à l’utilisateur d’effectuer sa recherche directement à
l’intérieur de l’application afin de lui éviter de copier/coller les liens, et
de changer d’application. Comment mettre en place ce moteur de recherche ? Une
API Youtube existe, et permet par le biais de lien au format spécial d’obtenir les résultats d’une requête de recherche sous la forme JSON (JavaScript Object Notation) qui est un format de texte générique équivalant au XML (eXtended Markup Language). Une fois la requête émise il suffit de \og parser\fg{} le résultat et de construire le modèle de donnée afin de générer un tableau. Voici comment l’on procède : cette méthode est appelée lorsque l’on a récuperéré les mots clés entrés par l’utilisateur dans la barre de recherche. Elle permet d’envoyer la requête HTTP au serveur Youtube, et de récupérer le résultat en le stockant dans un objet de type \emph{NSArray} (un tableau). Elle met à jour la vu du tableau par la suite. Si une erreur se produit elle affiche une alerte.

\begin{lstlisting}
(void)searchYoutubeVideosForTerm:(NSString *)term{
    term = [term stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSString* searchCall = [NSString stringWithFormat:@"http://gdata.youtube.com/feeds/api/videos?q=%@&max-results=50&alt=json", term];
    [JSONHTTPClient getJSONFromURLWithString: searchCall completion:^(NSDictionary *json, JSONModelError *err) {
        if (err) {
            [[[UIAlertView alloc] initWithTitle:@"Error" message:[err localizedDescription] delegate:nil cancelButtonTitle:@"Close"otherButtonTitles: nil] show];
            return;
        }else{
            NSDictionary *feed = [json objectForKey:@"feed"];
            videos = [feed objectForKey:@"entry"];
            [theTableView reloadData];
        }
        }];
}
\end{lstlisting}

Ainsi, le tableau \og video\fg{} contiendra toutes les données nécessaires pour identifier les vidéos qui correspondent au résultat des mots clés. Par la suite, lors de la construction ou la reconstruction de la vue du tableau (ce que l’utilisateur voit), on \emph{parse} chaque élement du tableau vidéo afin de sélectionner les informations que l’on souhaite afficher, à savoir le titre, l’image de la vidéo ainsi que l’URL (cette donnée n’est pas affiché mais stockée dans la cellule).

\bigskip

La méthode suivante est appelée à chaque création de cellule, elle renvoie une cellule pour générer le tableau.

\begin{lstlisting}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    /*...Déclaration de la cellule...*/
    
    //Parsing
    NSDictionary *media = [videos objectAtIndex:indexPath.row]; //On récupère le noeud d'index "i"
    NSString* url = [[[media objectForKey:@"link"]objectAtIndex:0]objectForKey:@"href"]; //On selectionne l'attribut href du premier noeud du noeud "link".
    NSString* title = [[media objectForKey:@"title"]objectForKey:@"$t"];
    NSString* thumbnail = [[[[media objectForKey:@"media$group"]objectForKey:@"media$thumbnail"]objectAtIndex:0]objectForKey:@"url"]; //On récupère le lien qui contient la photo illustrant la vidéo
    //On donne les valeurs aux différents attributs de la cellule
    cell.image.image = [self loadPhotoFromURL:[[NSURL alloc] initWithString:thumbnail]];
    cell.title.text = title;
    cell.url = url;
    //On retourne la cellule
    return cell;
}
\end{lstlisting}


Enfin, l’utilisateur sélectionne la vidéo qu’il désire visionner par un simple toucher. Une requête d’ajout de média est alors envoyée au serveur \emph{smartphone} avec en paramètre le lien qui correspond à la vidéo que l’utilisateur à choisi.

\bigskip

La méthode suivante est appelée lorsqu’une cellule est touchée :

\begin{lstlisting}
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    YouTubeCell *cell = (YouTubeCell *)[tableView cellForRowAtIndexPath:indexPath]; //On récupère la cellule
    NSString* request = [NSString stringWithFormat:@"Youtube:%@",cell.url]; //On construit la requete avec l\prime URL contenu dans la cellule
    [req sendRequest:request]; //On envoie la requête
    [self.navigationController pushViewController:svc animated:YES]; //On change de vue
}
\end{lstlisting}

\subsection{L'application du Raspberry-Pi}
L’application développée se situe au centre de l’architecture globale du projet. En effet, elle concentre la gestion de l’affichage des pages web via l’implémentation d’un serveur Tomcat, la communication avec les applications mobiles via le serveur \emph{smartphone}, la gestion des données et l’affichage de l’interface graphique.

\bigskip

Au centre de cette application se trouve la classe \emph{Manager}, passerelle entre les évènements des serveurs ou de l’interface graphique et OMXPlayer. C’est également cette classe qui va effectuer le traitement des différentes données (utilisateurs et média). A chaque fois qu’une requête est effectuée sur les pages web, sur les applications smartphones, où lors de la pression d’un bouton sur l’interface graphique, elle abouti à l’appel d’une méthode de la classe \emph{Manager}.

\bigskip

Pour lire les fichier vidéo ou audio, on utilise le lecteur OMXPlayer qui est un lecteur de vidéo et musique spécialement conçu pour le Raspberry-Pi. Nous avons créé une classe \emph{Player} qui correspond à une API pour OMXPlayer. On utilise les objets \emph{ProcessBuilder} permettant d'exécuter un programme en ligne de commande via Java. Lorsqu’on joue un fichier avec OMXPlayer, on peut interagir avec le lecteur en appuyant sur des touches (\og p\fg{} pour pause, \og q\fg{} pour stopper la lecture...). Il nous faut donc simuler les appuis touches pour pouvoir interagir avec le lecteur. C’est pourquoi, lors de la lecture d’un fichier, on commence par créer un \emph{pipe} qui nous permettra de simuler ces appuis touches. Le \emph{pipe} est connecté sur l’entrée standard de la commande OMXPlayer pour lire le fichier. On termine cette commande par un \og echo end\fg{}. Cela nous permettra de savoir quand la musique est terminée grâce à un\emph{ thread CheckPlaying} lancé en parallèle. Ce \emph{thread} lit continuellement la sortie de la commande exécutée et lorsqu’elle affiche \og \emph{end}\fg{}, il notifie la classe \emph{Manager} qui se chargera de lancer la commande pour lire la musique suivante.

\bigskip

Pour l’interface graphique de l’application, nous avons appliqué le \emph{pattern} MVC (Modèle, Vue, Controleur) permettant de différencier vue et traitement des données. La vue permet d’afficher les utilisateurs connectés, la liste de médias ajoutés, et également la musique en cours de lecture. A chaque évènement, seul les panels concernés sont mis à jour par la classe \emph{Manager}.

\bigskip

\begin{figure}[h!]
  \centering
    \includegraphics[width=0.8\textwidth]{architecture_materielle.png}
  \caption{Schéma simplifié de l'architecture du Raspberry-Pi}
\end{figure}
